package com.jkgj.skykingkong.tms.config;

import com.alibaba.fastjson.JSON;
import com.jkgj.skykingkong.tms.domain.model.rbac.User;
import com.jkgj.skykingkong.tms.domain.model.rbac.UserRepository;
import com.jkgj.skykingkong.tms.infrastructure.security.JsonUsernamePasswordAuthenticationFilter;
import com.jkgj.skykingkong.tms.infrastructure.security.User2Back;
import com.jkgj.skykingkong.tms.infrastructure.security.UserDetailsQueryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.IgnoredRequestCustomizer;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.cors.CorsUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Xuegui Yuan
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
  @Autowired
  private UserDetailsQueryService userDetailsQueryService;

  @Autowired
  private AuthenticationManager authenticationManager;

  @Bean
  public IgnoredRequestCustomizer optionsIgnoredRequestsCustomizer() {
    return configurer -> {
      List<RequestMatcher> matchers = new ArrayList<>();
      matchers.add(new AntPathRequestMatcher("/**", "OPTIONS"));
      configurer.requestMatchers(new OrRequestMatcher(matchers));
    };
  }

  /**
   * Configure order web security with authentication manager build
   */
  @Override
  public void configure(AuthenticationManagerBuilder auth) throws Exception {
    PasswordEncoder passwordEncoder = passwordEncoder();
    auth.userDetailsService(userDetailsQueryService).passwordEncoder(passwordEncoder);
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
        //开启CORS过滤器
        .cors()
        .and()
        //禁用CSRF保护
        .csrf().disable()
        .authorizeRequests()
        //不拦截PreflightRequest
        .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
        //配置那些路径可以不用权限访问
        .mvcMatchers("/login").permitAll()
        //任何访问都必须授权
        .anyRequest().authenticated()
        .and()
        .formLogin()
        //登陆成功后的处理，因为是API的形式所以不用跳转页面
        .successHandler(restAuthenticationSuccessHandler())
        //登陆失败后的处理
        .failureHandler(restAuthenticationFailureHandler())
        .and()
        .logout()
        // 登出成功后的处理
        .logoutSuccessHandler(restLogoutSuccessHandler())
        .and()
        //认证不通过后的处理
        .exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint());

    http.addFilterBefore(jsonUsernamePasswordAuthenticationFilter(),
        UsernamePasswordAuthenticationFilter.class);
  }

  @Bean
  public RestAuthenticationSuccessHandler restAuthenticationSuccessHandler() {



    return new RestAuthenticationSuccessHandler();
  }

  @Bean
  public RestAuthenticationFailureHandler restAuthenticationFailureHandler() {
    return new RestAuthenticationFailureHandler();
  }

  @Bean
  public RestLogoutSuccessHandler restLogoutSuccessHandler() {
    return new RestLogoutSuccessHandler();
  }

  @Bean
  public JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter() {
    JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter
        = new JsonUsernamePasswordAuthenticationFilter();
    jsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager);
    jsonUsernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(restAuthenticationSuccessHandler());
    jsonUsernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(restAuthenticationFailureHandler());
    return jsonUsernamePasswordAuthenticationFilter;
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    PasswordEncoder encoder = new BCryptPasswordEncoder(5);
    return encoder;
  }

  /**
   * 登陆成功后的处理
   */
  public static class RestAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    @Autowired
    UserRepository userRepository;

//    @Override
//    public void onAuthenticationSuccess(HttpServletRequest request,
//                                        HttpServletResponse response, Authentication authentication)
//        throws ServletException, IOException {
//
//      String username = SecurityContextHolder.getContext().getAuthentication().getName();
//      User user = userRepository.findUserByUsername(username);
//      request.getSession().setAttribute("username",user);
//      clearAuthenticationAttributes(request);
//    JSON.toJSON(user);
//    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response, Authentication authentication)
        throws ServletException, IOException {

        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        User user = userRepository.findUserByUsername(username);
        User2Back user2Back = new User2Back();
        user2Back.setId(user.getId());
        user2Back.setUsername(user.getUsername());
        user2Back.setMobile(user.getMobile());
        user2Back.setEmail(user.getEmail());
        user2Back.setName(user.getName());
        user2Back.setRoleCodes(user.getRoleCodes());
        user2Back.setPermissionCodes(user.getPermissionCodes());
        user2Back.setAdmin(user.isAdmin());
        user2Back.setEnabled(user.isEnabled());
        response.setHeader("Content-type", "text/json;charset=UTF-8");

        response.getWriter().print(JSON.toJSON(user2Back));
        clearAuthenticationAttributes(request);
      }
    }

  public static class RestAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response, AuthenticationException exception)
        throws IOException, ServletException {
      response.sendError(HttpServletResponse.SC_BAD_REQUEST,
          "认证失败:" + exception.getLocalizedMessage());
    }
  }

  /**
   * 登出成功后的处理
   */
  public static class RestLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {

    @Override
    public void onLogoutSuccess(HttpServletRequest request,
                                HttpServletResponse response, Authentication authentication)
        throws IOException, ServletException {
      //Do nothing!
    }
  }

  /**
   * 权限不通过的处理
   */
  public static class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException authException) throws IOException {
      response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
          "Authentication Failed: " + authException.getMessage());
    }
  }
}
